package com.share.finance.risk.rules.service.impl.core;

import com.alibaba.fastjson.JSON;
import com.share.common.utils.FileUtil;
import com.share.common.utils.HttpUtil;
import com.share.finance.risk.rules.constants.SysConstant;
import com.share.finance.risk.rules.model.db.Project;
import com.share.finance.risk.rules.service.IProjectService;
import com.share.finance.risk.rules.service.core.ContainerService;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.io.Resource;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.AgendaFilter;
import org.kie.internal.io.ResourceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class ContainerServiceImpl implements ContainerService {
    @Value("${rootUrl.drls}")
    private String drlsRootUrl;
    private static String SRC_MAIN_RESOURCES = "src" + SysConstant.PATH_SPLIT + "main" + SysConstant.PATH_SPLIT + "resources" + SysConstant.PATH_SPLIT;

    //静态公共对象
    private KieServices kieServices = KieServices.Factory.get();

    //存储不同数据产品的container
    private Map<String, KieContainer> containerMap = new HashMap<>();

    //不同产品的过滤器
    private Map<String, AgendaFilter> filterMap = new HashMap<>();

    @Value("${drlAdminFileHome}")
    private String drlAdminFileHome;

    @Autowired
    private IProjectService iProjectService;

    //初始化加载drl，并实例化Container
    @PostConstruct
    public void initAllContainer() {
        log.info("#####正在初始化容器#####");

        List<Project> projects = iProjectService.initProject();
        for (Project project : projects) {
            try {
                initContainer(project);
            } catch (Exception e) {
                log.error("舒适化容器失败project={}", JSON.toJSON(project));
            }
        }

        log.info("#####有效的数据产品-初始化完毕#####");
    }


    public void initContainer(Project p) {
        //1.组装路径
        String dirName = p.getProductCode() + SysConstant.PROJECT_SPLIT + p.getEventType() + SysConstant.PROJECT_SPLIT + p.getVersion();
        String drlFileName = dirName + SysConstant.DRL_FILE_SUFFIX;
        String path = SysConstant.EXECUTE_DRL_HOME + SysConstant.PATH_SPLIT + dirName + SysConstant.PATH_SPLIT + drlFileName;
        try {
            File file = new File(path);
            if (!file.exists()) {
                //2.从后台管理系统处下载drl文件
                downloadDrl(drlFileName, path);
            }

            //3.再次读取文件
            File drlFile = new File(path);
            if (drlFile.exists()) {
                String key = getMapKey(p);

                //4.初始化工程的container
                KieContainer container = createContainer(p, path);
                containerMap.put(key, container);

                //5.构建工程的过滤器(这里使用的是lamba构建实现类)---这里比较的是drl文件定义的包名
                String packageName = SysConstant.DRL_FILE_PACKAGE_PREFIX + p.getProductCode() + p.getEventType();
                AgendaFilter filter = match -> match.getRule().getPackageName().equals(packageName);
                filterMap.put(key, filter);
            }
        } catch (Exception e) {
            log.error("drl文件加载失败", e);
        }
    }

    /**
     * @param p    projectid值
     * @param path drl文件路径
     */
    public KieContainer createContainer(Project p, String path) {
        log.info("正在初始化工程path={},project={}", path, JSON.toJSONString(p));

        //1.生成该工程的maven坐标文件
        ReleaseId releaseId = getReleaseId(p);

        //2.创建kjar---KieModule里面是坐标和规则的资源文件
        KieModule kjar = createKjar(releaseId, path);

        //3.把kjar添加到知识库
        kieServices.getRepository().addKieModule(kjar);

        //发布规则(初始化容器)
        KieContainer container = kieServices.newKieContainer(releaseId);
        return container;
    }

    private KieModule createKjar(ReleaseId releaseId, String path) {
        //通过文件构建List<Resource>
        String virPath = new File(path).getName();
        log.info("虚拟路径virPath={},path={}", virPath, path);
        Resource resource = ResourceFactory.newFileResource(path).setSourcePath(virPath);//设置目标文件路径很重要,否则无法加载
        if (path.endsWith(".drl")) {
            resource.setResourceType(ResourceType.DRL);
        } else {
            resource.setResourceType(ResourceType.JAVA);
        }
        //创建这个resource对象很关键的

        List<Resource> resourceList = new LinkedList();
        resourceList.add(resource);

        return createKjar(releaseId, resourceList);
    }

    //resourceList 可以是java文件,也可以是drl文件
    private KieModule createKjar(ReleaseId releaseId, List<Resource> resourceList) {
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();

        //定义该内容再ks中的获取名字
        kieFileSystem.generateAndWritePomXML(releaseId);

        //规则文件写入ks文件中
        resourceList.stream().forEach(e -> kieFileSystem.write(e));

        //构建drl文件
        KieBuilder kb = kieServices.newKieBuilder(kieFileSystem).buildAll();
        String buildMsg = kb.getResults().toString();
        if (kb.getResults().hasMessages(Message.Level.ERROR)) {
            log.info("版本启动构建ERROR,buildMsg={}", buildMsg);
            throw new RuntimeException(buildMsg);
        }
        if (kb.getResults().hasMessages(Message.Level.INFO)) {
            log.info("版本启动构建INFO,buildMsg={}", buildMsg);
            throw new RuntimeException(buildMsg);
        }
        if (kb.getResults().hasMessages(Message.Level.WARNING)) {
            log.info("版本启动构建WARNING,buildMsg={}", buildMsg);
            throw new RuntimeException(buildMsg);
        }
        return kb.getKieModule();
    }

    private boolean removeContainer(Project p) {
        ReleaseId releaseId = getReleaseId(p);
        kieServices.getRepository().removeKieModule(releaseId);

        //这里不要删除了,多个版本会有问题的
        String key = getMapKey(p);
        containerMap.remove(key);
        filterMap.remove(key);

        return true;
    }


    //获取maven坐标
    private ReleaseId getReleaseId(Project p) {
        String groupId = SysConstant.DRL_FILE_PACKAGE_PREFIX + p.getProductCode() + p.getEventType();
        String artifactId = p.getProductCode() + p.getEventType();
        String version = p.getVersion();
        ReleaseId releaseId = getReleaseId(groupId, artifactId, version);
        return releaseId;
    }

    //获取maven坐标
    private ReleaseId getReleaseId(String groupId, String artifactId, String version) {
        if (!groupId.contains(SysConstant.DRL_FILE_PACKAGE_PREFIX)) {
            groupId = SysConstant.DRL_FILE_PACKAGE_PREFIX + groupId;
        }
        return kieServices.newReleaseId(groupId, artifactId, version);
    }

    private String getMapKey(Project p) {
        String key = p.getProductCode() + "-" + p.getEventType();
        return key;
    }

    //执行规则---外部标准入口
    @Override
    public int exeRule(Project p, Map inputDataMap) {
        String key = getMapKey(p);
        KieContainer container = containerMap.get(key);
        AgendaFilter filter = filterMap.get(key);
        return exeRule(container, filter, inputDataMap);
    }

    //执行规则
    private int exeRule(KieContainer container, AgendaFilter filter, Map inputDataMap) {
        KieSession kieSession = container.newKieSession();

        //加载数据
        kieSession.insert(inputDataMap);

        int hitRuleCount = -1;//命中规则的数量
        if (filter == null) {
            hitRuleCount = kieSession.fireAllRules();
        } else {//执行指定包名的drl文件
            hitRuleCount = kieSession.fireAllRules(filter);
        }

        kieSession.dispose();//关闭session
        return hitRuleCount;
    }

    //执行规则---外部测试入口
    @Override
    public int test(List<Resource> resourceList, Map<String, Object> data) {
        //生成releaseId的maven坐标文件
        ReleaseId releaseId = getReleaseId(SysConstant.DRL_FILE_PACKAGE_PREFIX + "test", "test", "1.0.0");

        //向坐标文件中写入内容
        KieModule kieModule = createKjar(releaseId, resourceList);

        //把kjar加载Repository
        kieServices.getRepository().addKieModule(kieModule);

        //发布规则(初始化容器)
        KieContainer container = kieServices.newKieContainer(releaseId);

        //执行规则
        int hitRuleCount = exeRule(container, null, data);

        //销毁运行环境---每个工程都有自己的容器
        container.dispose();

        return hitRuleCount;
    }

    /**
     * 从后台管理系统下载文件,并存在指定路径
     *
     * @param drlFileName mayi#yx#v1
     * @param path
     */
    private void downloadDrl(String drlFileName, String path) {
        File desFile = new File(path);

        //创建目录
        boolean mkdirs = new File(desFile.getParent()).mkdirs();

        File srcDir = new File(drlAdminFileHome);
        if (srcDir.exists() && srcDir.isDirectory()) {
            //本地复制
            List<File> files = FileUtil.listFile(drlAdminFileHome);
            for (File file : files) {
                if (file.getAbsolutePath().endsWith(drlFileName)) {
                    FileUtil.copyFile(file, desFile);
                    log.info("正在下载drl文件,从目录复制drlAdminFileHome={},drlFileName={},path={}", drlAdminFileHome, drlFileName, path);
                    break;
                }
            }
        } else {
            //从别的机器上去下载
            String name = desFile.getName();
            String url = name.replace(SysConstant.DRL_FILE_SUFFIX, "") + "/" + name;
            url = drlsRootUrl + "/" + url.replaceAll("#", "%23");
            try {
                log.info("正在下载drl文件,url={},path={}", url, path);
                String content = HttpUtil.get(url);
                FileUtil.write(path, content);
            } catch (Exception e) {
                log.error("文件下载失败url={}", url, e);
            }
        }
    }
}

